from learning_curve import plot_learning_curve
import seaborn as sn
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.model_selection import ShuffleSplit
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
import sys

digits = load_digits()
#Python\Python310\Lib\site-packages\sklearn\datasets\__init__.py
#   from ._base import load_digits
#       Python\Python310\Lib\site-packages\sklearn\datasets\_base.py
#           def load_digits(*, n_class=10, return_X_y=False, as_frame=False):
#               load_gzip_compressed_csv_data(data_file_name="digits.csv.gz")
#                   Python\Python310\Lib\site-packages\sklearn\datasets\data\digits.csv.gz
#将csv文件解压出来，是一个1797行,65列的二维数据
#其中每一行的前64列，是图片的本体数据，使用的时候分割成8行*8列的二维数据
#而第一行的最后一列（第65列），是该图片所标识的数字（可以看出一定是0-9之前的数字）
X = digits.data #观测数据
y = digits.target #观测结果
#Python\Python310\Lib\site-packages\sklearn\datasets\_base.py
#load_digits函数中
#   data = load_gzip_compressed_csv_data("digits.csv.gz")
#   target = data[:, -1].astype(int, copy=False) #截取所有行，最后一列
#   flat_data = data[:, :-1]    #截取所有行，第0列到倒数第二列
#   return Bunch(data=flat_data, target=target)

#print("X.shape:", X.shape) #X.shape: (1797, 64)
#print("y.shape:", y.shape) #y.shape: (1797,)

# print("X 最大元素:", np.max(X)) #X 最大元素: 16.0  #round(255/16.0)=16  可以看出原始数据 是将单色值0-255，又进行了缩小16倍进行保存的
# print("X 每一列最大元素:", np.max(X, axis=0)) #X 每一列最大元素: [ 0.  8. 16. 16. 16. 16. 16. 15.  2. 16. 16. 16. 16. 16. 16. 12.  2. 16. 16. 16. 16. 16. 16.  8.  1. 15. 16. 16. 16. 16. 15.  1.  0. 14. 16. 16. 16. 16. 14.  0.  4. 16. 16. 16. 16. 16. 16.  6.  8. 16. 16. 16. 16. 16. 16. 13.  1.  9. 16. 16. 16. 16. 16. 16.]
# print("X 每一行最大元素:", np.max(X, axis=1)) #X 每一行最大元素: [15. 16. 16. ... 16. 16. 16.]


def printNpaInf(a, varName):
    ta = type(a)
    print("%s, type:%s" % (varName, ta))
    if "<class 'numpy.ndarray'>" == str(ta).strip():
        print("  shape:", a.shape, ", dtype:", a.dtype, ", max:", np.max(a), ", min:", np.min(a))

def sampleShow(image):
    simg = image * 16
    simg = np.where(simg > 255, 255, simg)
    d2 = simg.reshape((8, 8))  # 从一维变为二维，这样才能被显示
    print("d2.shape:", d2.shape)  # 查看是否是二维数组
    print("np.max(image):", np.max(image), ", np.max(simg):", np.max(simg))
    print("np.min(image):", np.min(image), ", np.min(simg):", np.min(simg))
    # print(d2)
    plt.imshow(d2)  # 显示灰色图像
    plt.show()


def standard_demo(data):
    transfer = StandardScaler() #Scikit-learn 库中的一个预处理类
    data_new = transfer.fit_transform(data) #对数据进行标准化处理（即将数据按照均值为0、标准差为1进行缩放
    #在这里提到的标准差是指数据集中各个数据点与数据集的均值之间的差异。标准差是衡量数据的离散程度的一种统计指标，它表示数据点相对于数据集均值的平均偏差。
    #具体地说，计算标准差的步骤如下：
    #    计算数据集的均值（平均值）：将数据集中所有数据点的值相加，然后除以数据点的个数。
    #    计算每个数据点与均值的差值：将每个数据点的值减去均值，得到每个数据点与均值之间的差异。
    #    计算差值的平方：将每个差值的结果平方，以消除差值的正负影响。
    #    计算平方差的平均值：将所有平方差相加，然后除以数据点的个数。
    #    计算平均平方差的平方根：将平均平方差的结果取平方根，得到标准差。
    #标准差表示数据点相对于均值的平均偏差大小，它越大表示数据点相对于均值的差异程度越大，数据集的离散程度越高。标准差为1意味着数据点相对于均值的平均偏差为1，即数据集的离散程度相对较小。

    print("type(data_new):%s" % type(data_new))
    print("data_new.shape:", data_new.shape)
    print("np.min(data_new):", np.min(data_new), ", np.max(data_new):", np.max(data_new))
    print("np.mean(data_new):%.6lf" % np.mean(data_new))
    v = np.sum(data_new ** 2)
    print("np.sum(data_new ** 2):", v)
    c = data_new.shape[0] * data_new.shape[1]
    print("data_new.shape[0] * data_new.shape[1]:", c)
    print("标准差=", v / c) #0.953125 —_- ai说，浮点运算正常偏差。。

    return data_new


def pca_demo(data):
    print("before pca data.shape:", data.shape)
    transfer = PCA(n_components=0.92)
    data_new = transfer.fit_transform(data) #降维
    print("after pca data type:", type(data_new)) #numpy.ndarray
    #print(data_new)
    print("after pca data.shape:", data_new.shape)
    return data_new

        

def knn_demo(data, label):
    # 划分数据集
    X_train, X_test, y_train, y_test = train_test_split(data, label, random_state=6)
    printNpaInf(X_train, "X_train")
    printNpaInf(X_test, "X_test")
    printNpaInf(y_train, "y_train")
    printNpaInf(y_test, "y_test")
    
    
    # 训练模型
    estimate = KNeighborsClassifier(n_neighbors=10)
    estimate.fit(X_train, y_train)  # 模型构建好了
    # 模型评估的两种方法：1：直接比对预测值与真实值；
    y_predict = estimate.predict(X_test)
    print("直接比对预测值与真实值：\n", y_test == y_predict)
    
    # 2：计算准确率
    score = estimate.score(X_test, y_test)
    print("准确率为：\n", score)
    sys.exit()

    # 绘制学习曲线!!!!
    cv = ShuffleSplit(n_splits=10, test_size=0.2, random_state=0)  # 10折交叉验证
    fig, ax = plt.subplots(1, 1, figsize=(10, 6), dpi=144)
    plot_learning_curve(ax, estimate, "Learn Curve",
                        X_train, y_train, ylim=(0.0, 1.01), cv=cv)
    plt.show()
    # 混淆矩阵
    cm = confusion_matrix(y_test, y_predict)
    print(cm)
    # 可视化显示混淆矩阵
    # annot = True 显示数字 ，fmt参数不使用科学计数法进行显示
    ax = sn.heatmap(cm, annot=True, fmt='.20g')
    ax.set_title('confusion matrix')  # 标题
    ax.set_xlabel('predict')  # x轴
    ax.set_ylabel('true')  # y轴
    plt.show()
    # 计算精确率与召回率
    report = classification_report(y_test, y_predict, labels=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
                                   target_names=['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])
    print(report)


# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
    # 查看数据集，以图片显示
    print(X.shape, y.shape) #(1797, 64) (1797,)
    print("X.dtype:", X.dtype, ", y.dtype:", y.dtype) #X.dtype: float64 , y.dtype: int32
    #print(X, y)
    #sampleShow(X[1795]) #将倒数第二个图显示
    #print(y[1795])

    # 数据集预处理（标准化、特征降维）
    X_new = standard_demo(X)
    X_new = pca_demo(X_new)
    print(X_new.shape)  # （1797, 64)->(1797, 34) 第二维从64降到了34
    # 机器学习建模
    # 调参
    # 模型评估
    # 学习曲线
    knn_demo(X_new, y)  # 数据已经准备好了
